<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1, maximum-scale=1.0, user-scalable=no, viewport-fit=cover, shrink-to-fit=no, minimal-ui">
    <title>Demo: Auto-Complete</title>
    <style>
        body{
            padding: 0;
            margin: 0 10px;
            font-family: Helvetica, Arial, sans-serif;
            font-size: 14px;
        }
        #header{
            position: fixed;
            top: 10px;
            left: 10px;
            width: 100%;
            display: flex;
            justify-content: flex-start;
            flex-direction: row;
            max-width: 500px;
            table-layout: fixed;
            background-color: #fff;
            z-index: 1;
        }
        input[type="text"]{
            width: 100%;
            border: 1px solid #ddd;
            border-radius: 4px;
            outline: none;
            background-color: transparent;
            position: absolute;
            -webkit-appearance: none;
            z-index: 1;
        }
        #autocomplete{
            color: #999;
            background-color: #f5f5f5;
            pointer-events: none;
        }
        #select-prev,
        #select-next{
            z-index: 0;
        }
        input{
            padding:7px 5px;
            box-sizing: border-box;
        }
        #suggestions{
            position: relative;
            top: 50px;
        }
        #suggestions div{
            padding: 10px 8px;
            border-bottom: 1px solid #ddd;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
            width: calc(100% - 16px);
        }
        #suggestions b{
            color: blue;
        }
        #suggestions:empty:before{
            content: "No resuls found";
            font-size: 12px;
            color: #999;
            margin-left: 7px;
        }
        #info{
            position: absolute;
            bottom: 7px;
            left: 12px;
            color: #888;
        }
        label{
            width: 100px;
            height: 32px;
            display: flex;
            align-items: center;
            margin: 0 10px;
        }
        @media only screen and (max-width: 600px) {
            body{
                font-size: 12px;
            }
        }
    </style>
</head>
<body>
<div id="header">
    <form style="position: relative; width: 100%;">
        <input type="text" id="select-prev" tabindex="1">
        <input type="text" id="autocomplete" readonly>
        <input type="text" id="userinput" tabindex="2" placeholder="Search by movie title ..." autocorrect="off" spellcheck="false" autocomplete="off" autofocus>
        <input type="text" id="select-next" tabindex="3">
    </form>
    <label>
        <input type="checkbox" id="suggest" checked> Suggest
    </label>
</div>
<!-- Iterate through results by arrow keys -->
<div id="suggestions"></div>
<script type="module">

    // Features:
    // 1. Instant Search
    // 2. Auto-Complete
    // 3. Suggestions
    // 4. Query Cache
    // 5. Result Highlighting
    // 6. Tokenizer
    // 7. Encoder
    // 8. Document Index (Field-Search)
    // 9. Keyboard Navigation

    import { Document } from "https://cdn.jsdelivr.net/gh/nextapps-de/flexsearch@master/dist/flexsearch.compact.module.min.js";
    import data from "https://cdn.jsdelivr.net/gh/nextapps-de/flexsearch@master/demo/data/movies.js";

    // for "Result Highlighting" it requires the usage of a Document Index
    // also when just adding id-content-pairs to a single index
    const index = new Document({
        document: {
            // storing documents is required in this example
            store: true,
            index: [{
                field: "title",
                // important: a forward tokenizer is minimum
                // required by an instant search
                tokenize: "forward"
            }]
        }
    });

    // The movies.js data used in this example is an array of strings.
    // Since result highlighting needs a Document Index, the data
    // has to be simply upgraded to a pseudo document.

    for(let i = 0; i < data.length; i++){
        // pass a json-like object
        index.add(i, {
            "title": data[i]
        });
    }

    const suggestions = document.getElementById("suggestions");
    const autocomplete = document.getElementById("autocomplete");
    const userinput = document.getElementById("userinput");
    const suggest = document.getElementById("suggest");
    const select_prev = document.getElementById("select-prev");
    const select_next = document.getElementById("select-next");
    let results = [];
    let iterate = 0;

    userinput.addEventListener("input", show_results, true);
    userinput.addEventListener("keyup", accept_autocomplete, true);
    userinput.addEventListener("keydown", iterate_selected, true);
    suggest.addEventListener("change", toggle_suggest, true);
    suggestions.addEventListener("click", accept_suggestion, true);

    select_prev.addEventListener("focus", function(){
        iterate_selected({ key: "ArrowUp" });
        userinput.focus();
    }, true);

    select_next.addEventListener("focus", function(){
        iterate_selected({ key: "ArrowDown" });
        userinput.focus();
    }, true);

    function toggle_suggest(){
        // changing any of the search options requires
        // deletion of cached results (when using cache)
        index.cache.clear();
        show_results();
    }

    function show_results(){

        // the cache is a perfect addition
        // for an instant search on keypress

        results = userinput.value ? index.searchCache({
            query: userinput.value,
            suggest: suggest.checked,
            limit: 25,
            pluck: "title",
            enrich: true,
            highlight: "<b>$1</b>"
        }) : [];

        let entry, childs = suggestions.childNodes;
        let i = 0, len = results.length;

        for(; i < len; i++){

            entry = childs[i];

            if(!entry){
                entry = document.createElement("div");
                suggestions.appendChild(entry);
            }

            entry.innerHTML = results[i].highlight; //data[results[i]];
        }

        while(childs.length > len){
            suggestions.removeChild(childs[i]);
        }

        select_result(0);
        iterate_selected();
    }

    function iterate_selected(event){

        let index = iterate;

        if(event){
            const key = event.key;
            if(key === "ArrowUp"){
                if(iterate > 0){
                    select_result(--index);
                }
                event.preventDefault &&
                event.preventDefault();
            }
            else if(key === "ArrowDown"){
                if(iterate < results.length){
                    select_result(++index);
                }
                event.preventDefault &&
                event.preventDefault();
            }
        }

        let value = userinput.value;
        let first_result = results && results[index] && data[results[index].id];
        let match = first_result && first_result.toLowerCase().indexOf(value.toLowerCase());

        if(first_result && (match > -1)){
            autocomplete.value = value + first_result.substring(match + value.length);
            autocomplete.current = first_result;
        }
        else{
            autocomplete.value = autocomplete.current = value;
        }
    }

    function select_result(index){

        let node = suggestions.childNodes[iterate];
        node && (node.style.backgroundColor = "");

        iterate = index;

        node = suggestions.childNodes[iterate];
        node && (node.style.backgroundColor = "rgba(0, 0, 255, 0.1)");
    }

    function accept_autocomplete(event){

        const key = (event || window.event).keyCode;

        if(key === 13 || key === 39) {
            const node = suggestions.childNodes[iterate];
            if(!node) return;
            userinput.value = autocomplete.value = node.textContent;
            suggestions.textContent = "";
        }
    }

    function accept_suggestion(event){

        const target = (event || window.event).target;
        userinput.value = autocomplete.value = target.textContent;
        suggestions.textContent = "";

        return false;
    }
</script>
</body>
</html>
